I have created a wrapped NSTextField to allow for keystroke validation.
class ValidatableTextField: NSTextField, NSTextViewDelegate
{
var validationClosure: ((ValidatableTextField, NSRange, String?) -> Bool)?
func textView(_ textView: NSTextView, shouldChangeTextIn affectedCharRange: NSRange, replacementString: String?) -> Bool
{
return validationClosure?(self, affectedCharRange, replacementString) ?? true
}
}
struct CurrencyTextField<rootT : AnyObject, valueT> : NSViewRepresentable
{
private var object: rootT?
private var keyPath: ReferenceWritableKeyPath<rootT, valueT>?
func makeCoordinator() -> Coordinator
{
return Coordinator(object, keyPath: keyPath)
}
init(object: rootT?, keyPath: ReferenceWritableKeyPath<rootT, valueT>?)
{
self.object = object
self.keyPath = keyPath
}
func makeNSView(context: Context) -> ValidatableTextField
{
let textField = ValidatableTextField()
textField.validationClosure = context.coordinator.validationClosure
return textField
}
func updateNSView(_ nsView: ValidatableTextField, context: Context)
{
guard let object = object,
let keyPath = keyPath else
{
return
}
nsView.stringValue = "\(object[keyPath: keyPath])"
}
class Coordinator : NSObject
{
var object: rootT?
var keyPath: ReferenceWritableKeyPath<rootT, valueT>?
init(_ object: rootT?, keyPath: ReferenceWritableKeyPath<rootT, valueT>?)
{
self.object = object
self.keyPath = keyPath
}
lazy var validationClosure: ((ValidatableTextField, NSRange, String?) -> Bool) =
{
textField, range, replacementString in
if replacementString!.isEmpty
{
return true
}
return replacementString?.contains(where:
{
(char: Character) -> Bool in
return char.isNumber || String(char) == Locale.autoupdatingCurrent.decimalSeparator
}) ?? false
}
}
}
struct ContentView: View
{
@ObservedObject var person: Person
var body: some View
{
VStack
{
Spacer()
Text(person.name)
HStack
{
Spacer(minLength: 100)
CurrencyTextField<Person, Double>(object: person, keyPath: \.bankBalance)
Text("\(person.bankBalance)")
Spacer(minLength: 100)
}
Button(action:
{
self.person.bankBalance = 65.43
})
{
Text("Change bank balance")
}
Spacer()
}
}
}
My problem is that I am setting the bankBalance property on the Person and the Text next to the TextField is being updated but my CurrencyTextField is not.
I may have 30 years of programming experience, including quite a few with Swift but SwiftUI, beyond the simplest of examples, is proving a bit difficult. What am I missing here please?